﻿using FrameworkCore.Extensions;
using FrameworkCore.Migrations;
using FrameworkCore.Migrations.Data;
using FrameworkCore.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using System.Linq.Expressions;
using System.Linq.Dynamic.Core;
using FrameworkCore.Interfaces;

namespace FrameworkCore.Repositories;

#nullable disable

public abstract partial class RepositoryBase<TEntity> : IRepository<TEntity> where TEntity : BaseEntity
{
    private readonly IAiBLSmartEduDataProvider _dataProvider;
    private readonly ICurrentUserService _currentUserService;

    protected RepositoryBase(
        IAiBLSmartEduDataProvider dataProvider,
        ICurrentUserService currentUserService)
    {
        _dataProvider = dataProvider;
        _currentUserService = currentUserService;
    }

    public virtual void Delete(TEntity entity)
    {
        SetAuditableEntityFields(entity, OperationType.Delete);
        if (entity is ISoftDeletedEntity deletableEntity)
        {
            _dataProvider.UpdateEntity(entity);
        }
        else
        {
            _dataProvider.DeleteEntity(entity);
        }
        _dataProvider.SaveChanges();
    }

    public virtual int Delete(Expression<Func<TEntity, bool>> predicate)
    {
        return _dataProvider.BulkDeleteEntities(predicate);
    }

    public virtual async Task DeleteAsync(
        TEntity entity,
        CancellationToken cancellationToken = default)
    {
        SetAuditableEntityFields(entity, OperationType.Delete);
        if (entity is ISoftDeletedEntity deletableEntity)
        {
            _dataProvider.DeleteEntity(entity);
        }
        else
        {
            _dataProvider.UpdateEntity(entity);
        }
        await _dataProvider.SaveChangesAsync(cancellationToken);
    }

    public virtual async Task DeleteAsync(
        IList<TEntity> entities,
        CancellationToken cancellationToken = default)
    {
        foreach (TEntity entity in entities) 
        {
            SetAuditableEntityFields(entity, OperationType.Delete);
            if (entity is ISoftDeletedEntity deletableEntity)
            {
                _dataProvider.UpdateEntity(entity);
            }
            else
            {
                _dataProvider.DeleteEntity(entity);
            }
        }

        //_dataProvider.BulkDeleteEntities(entities);
        await _dataProvider.SaveChangesAsync(cancellationToken);
    }

    public virtual async Task<int> DeleteAsync(
        Expression<Func<TEntity, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        return await _dataProvider.BulkDeleteEntitiesAsync(predicate);
    }

    public virtual IList<TEntity> GetAll(
        Func<IQueryable<TEntity>,
            IQueryable<TEntity>> func = null,
        bool includeDeleted = false)
    {
        var query = AddDeletedFilter(includeDeleted);

        query = func != null ? func(query) : query;

        return query.ToList();
    }

    public virtual async Task<IList<TEntity>> GetAllAsync(
        Func<IQueryable<TEntity>, IQueryable<TEntity>> func = null,
        bool includeDeleted = false, CancellationToken cancellationToken = default)
    {
        var query = AddDeletedFilter(includeDeleted);

        query = func != null ? func(query) : query;

        return await query.ToListAsync(cancellationToken);
    }

    public virtual async Task<PagedList<TEntity>> GetAllPagedAsync(
        Func<IQueryable<TEntity>, IQueryable<TEntity>> func = null,
        int pageIndex = 0,
        int pageSize = int.MaxValue,
        bool getOnlyTotalCount = false,
        bool includeDeleted = false, CancellationToken cancellationToken = default)
    {
        var query = AddDeletedFilter(includeDeleted);

        query = func != null ? func(query) : query;

        return await query.ToPagedListAsync(pageIndex, pageSize, getOnlyTotalCount, cancellationToken);
    }

    public virtual async Task<PagedList<TEntity>> GetAllPagedAsync(
        Func<IQueryable<TEntity>, IQueryable<TEntity>> func = null,
        PaginationFilter filter = null,
        bool getOnlyTotalCount = false,
        bool includeDeleted = false,
        CancellationToken cancellationToken = default)
    {
        var query = AddDeletedFilter(includeDeleted);

        query = func != null ? func(query) : query;

        query = query.OrderBy($"{filter.OrderBy} {filter.SortDirection}");

        return await query.ToPagedListAsync(filter.PageNumber, filter.PageSize, getOnlyTotalCount, cancellationToken);
    }

    public virtual IOrderedQueryable<TEntity> GetOrderedQueryable(
        Func<IQueryable<TEntity>, IQueryable<TEntity>> func = null,
        PaginationFilter filter = null,
        bool includeDeleted = false)
    {
        var query = AddDeletedFilter(includeDeleted);

        query = func != null ? func(query) : query;

        if (filter != null) 
        {
            query = query.OrderBy($"{filter.OrderBy} {filter.SortDirection}");
        }

        return (IOrderedQueryable<TEntity>)query;
    }

    public virtual TEntity GetById(long? id, bool includeDeleted = false)
    {
        var query = AddDeletedFilter(includeDeleted);

        query = query.Where(x => x.Id == id);

        return query.FirstOrDefault();
    }

    public virtual Task<TEntity> GetByIdAsync(
        long? id,
        bool includeDeleted = false, CancellationToken cancellationToken = default)
    {
        var query = AddDeletedFilter(includeDeleted);

        query = query.Where(x => x.Id == id);

        return query.FirstOrDefaultAsync(cancellationToken);
    }

    public virtual async Task<IList<TEntity>> GetByIdsAsync(
        IList<long> ids,
        bool includeDeleted = false, CancellationToken cancellationToken = default)
    {
        var query = AddDeletedFilter(includeDeleted);

        query = query.Where(x => ids.Contains(x.Id));

        return await query.ToListAsync(cancellationToken);
    }

    public virtual void Insert(TEntity entity)
    {
        SetAuditableEntityFields(entity, OperationType.Create);
        _dataProvider.InsertEntity(entity);
        _dataProvider.SaveChanges();
    }

    public virtual void Insert(IList<TEntity> entities)
    {
        foreach (var entity in entities) 
        {
            SetAuditableEntityFields(entity, OperationType.Create);
        }
        _dataProvider.BulkInsertEntities(entities);
        _dataProvider.SaveChanges();
    }

    public virtual async Task InsertAsync(
        TEntity entity,
        CancellationToken cancellationToken = default)
    {
        SetAuditableEntityFields(entity, OperationType.Create);
        await _dataProvider.InsertEntityAsync(entity);
        await _dataProvider.SaveChangesAsync(cancellationToken);
    }

    public virtual async Task<TEntity> InsertAndGetAsync(
    TEntity entity,
    CancellationToken cancellationToken = default)
    {
        SetAuditableEntityFields(entity, OperationType.Create);
        await _dataProvider.InsertEntityAsync(entity);
        await _dataProvider.SaveChangesAsync(cancellationToken);
        return entity;
    }


    public virtual async Task InsertAsync(
        IList<TEntity> entities,
        CancellationToken cancellationToken = default)
    {
        foreach (var entity in entities)
        {
            SetAuditableEntityFields(entity, OperationType.Create);
        }
        await _dataProvider.BulkInsertEntitiesAsync(entities);
        await _dataProvider.SaveChangesAsync(cancellationToken);
    }

    public virtual IQueryable<TEntity> Query(
        Expression<Func<TEntity, bool>> predicate,
        bool includeDeleted = false)
    {
        var query = AddDeletedFilter(includeDeleted);

        query = query.Where(predicate);

        return query;
    }

    public virtual void Update(TEntity entity)
    {
        SetAuditableEntityFields(entity, OperationType.Update);
        _dataProvider.UpdateEntity(entity);
    }

    public virtual void Update(IList<TEntity> entities)
    {
        foreach (var entity in entities) 
        {
            SetAuditableEntityFields(entity, OperationType.Update);
        }
        _dataProvider.UpdateEntities(entities);
        _dataProvider.SaveChanges();
    }

    public virtual void Update(
    Expression<Func<SetPropertyCalls<TEntity>, SetPropertyCalls<TEntity>>> setPropertyCalls,
    Expression<Func<TEntity, bool>> predicate)
    {
        //_dataProvider.BulkUpdateEntities(ModifySetAuditablePropertyCalls(setPropertyCalls), predicate);
        _dataProvider.BulkUpdateEntities(setPropertyCalls, predicate);
    }

    public virtual async Task UpdateAsync(
        TEntity entity,
        CancellationToken cancellationToken = default)
    {
        SetAuditableEntityFields(entity, OperationType.Update);

        _dataProvider.UpdateEntity(entity);
        await _dataProvider.SaveChangesAsync(cancellationToken);
    }

    public virtual async Task UpdateAsync(
        IList<TEntity> entities,
        CancellationToken cancellationToken = default)
    {
        foreach (var entity in entities)
        {
            SetAuditableEntityFields(entity, OperationType.Update);
        }

        _dataProvider.UpdateEntities(entities);
        await _dataProvider.SaveChangesAsync(cancellationToken);
    }

    public virtual async Task UpdateAsync(
        Expression<Func<SetPropertyCalls<TEntity>, SetPropertyCalls<TEntity>>> setPropertyCalls,
        Expression<Func<TEntity, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        // 执行更新操作
        await _dataProvider.BulkUpdateEntitiesAsync(setPropertyCalls, predicate, cancellationToken);
    }

    /// <summary>
    /// 根据需要，向查询中添加包含 <see cref="ISoftDeletedEntity"/> 实体的 "deleted" 过滤器。
    /// </summary>
    /// <param name="query">实体查询</param>
    /// <param name="includeDeleted">是否包含已删除的条目</param>
    /// <returns>实体查询</returns>
    protected virtual IQueryable<TEntity> AddDeletedFilter(
        IQueryable<TEntity> query,
        in bool includeDeleted)
    {
        if (!includeDeleted)
            return query;

        if (typeof(TEntity).GetInterface(nameof(ISoftDeletedEntity)) == null)
            return query;

        return query.IgnoreQueryFilters();
    }

    /// <summary>
    /// 根据需要，向查询中添加包含 <see cref="ISoftDeletedEntity"/> 实体的 "deleted" 过滤器。
    /// </summary>
    /// <param name="includeDeleted">是否包含已删除的条目</param>
    /// <returns></returns>
    protected virtual IQueryable<TEntity> AddDeletedFilter(bool includeDeleted)
    {
        var query = _dataProvider.Query<TEntity>();

        if (!includeDeleted)
            return query;

        if (typeof(TEntity).GetInterface(nameof(ISoftDeletedEntity)) == null)
            return query;

        return query.IgnoreQueryFilters();
    }

    /// <summary>
    /// 审计
    /// </summary>
    /// <param name="entity">实体</param>
    /// <param name="operationType">操作类型</param>
    protected virtual void SetAuditableEntityFields(TEntity entity, OperationType operationType)
    {
        switch (operationType)
        {
            case OperationType.Create:
                if (entity is BaseAuditableEntity auditableCreateEntity)
                {
                    auditableCreateEntity.Created = DateTime.Now;
                    auditableCreateEntity.CreatedBy = _currentUserService.UserId;
                }
                break;

            case OperationType.Update:
                if (entity is BaseAuditableEntity auditableUpdateEntity)
                {
                    auditableUpdateEntity.Updated = DateTime.Now;
                    auditableUpdateEntity.UpdatedBy = _currentUserService.UserId;
                }
                break;

            case OperationType.Delete:
                if (entity is BaseAuditableEntity auditableEntity)
                {
                    auditableEntity.Updated = DateTime.Now;
                    auditableEntity.UpdatedBy = _currentUserService.UserId;
                }
                if (entity is ISoftDeletedEntity deletableEntity)
                {
                    deletableEntity.IsDeleted = true;
                }
                break;
        }
    }

    protected Expression<Func<SetPropertyCalls<TEntity>, SetPropertyCalls<TEntity>>> ModifySetAuditablePropertyCalls(
        Expression<Func<SetPropertyCalls<TEntity>, SetPropertyCalls<TEntity>>> original)
    {
        // 检查 TEntity 是否是 BaseAuditableEntity 的派生类
        if (!typeof(BaseAuditableEntity).IsAssignableFrom(typeof(TEntity)))
        {
            return original; // 如果不是，直接返回原始表达式
        }

        var parameter = original.Parameters[0];
        var bindings = new List<MemberBinding>();

        // 获取原有的绑定
        var memberInitExpression = (MemberInitExpression)original.Body;
        foreach (var binding in memberInitExpression.Bindings)
        {
            if (binding is MemberBinding memberBinding)
            {
                bindings.Add(memberBinding); // 添加原有绑定
            }
        }

        // 根据条件添加新的绑定
        bindings.Add(Expression.Bind(
            typeof(SetPropertyCalls<TEntity>).GetProperty("Updated"),
            Expression.Constant(DateTime.Now)));
        bindings.Add(Expression.Bind(
            typeof(SetPropertyCalls<TEntity>).GetProperty("UpdatedBy"),
            Expression.Constant(_currentUserService.UserId)));

        // 创建新的 MemberInitExpression
        var newBody = Expression.MemberInit(Expression.New(typeof(SetPropertyCalls<TEntity>)), bindings);
        return Expression.Lambda<Func<SetPropertyCalls<TEntity>, SetPropertyCalls<TEntity>>>(newBody, parameter);
    }
}
